package com.fowo.api.flow.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fowo.api.flow.constant.WorkFlowConstants;
import com.fowo.api.flow.entity.ProcessTemplates;
import com.fowo.api.flow.entity.TemplateGroup;
import com.fowo.api.flow.mapper.ProcessTemplatesMapper;
import com.fowo.api.flow.model.ChildNode;
import com.fowo.api.flow.model.dto.FlowEngineDTO;
import com.fowo.api.flow.model.SettingsInfo;
import com.fowo.api.flow.model.vo.TemplateVo;
import com.fowo.api.flow.service.ProcessTemplateService;
import com.fowo.api.flow.service.TemplateGroupService;
import com.fowo.api.flow.utils.BpmnModelUtils;
import com.fowo.api.flow.utils.DingDingUtil;
import com.fowo.api.flow.utils.IdWorker;
import com.fowo.api.user.model.JwtUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.compress.utils.Lists;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.flowable.bpmn.BpmnAutoLayout;
import org.flowable.bpmn.model.Process;
import org.flowable.bpmn.model.*;
import org.flowable.common.engine.api.FlowableException;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.delegate.ExecutionListener;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.lang.reflect.InvocationTargetException;
import java.util.*;

/**
 * @fileName: ProcessTemplateServiceImpl
 * @Description: 代码目的，作用，如何工作
 * @Author: pzh
 * @Date: 2023/3/29 0029 15:34
 */
@Slf4j
@Service
public class ProcessTemplateServiceImpl extends ServiceImpl<ProcessTemplatesMapper, ProcessTemplates> implements
        ProcessTemplateService {

    @Resource
    private IdWorker idWorker;

    @Resource
    private RepositoryService repositoryService;

    @Resource
    private TemplateGroupService templateGroupService;

    @Resource
    private DingDingUtil dingDingUtil;

    @Override
    public ProcessTemplates getFormTemplateById(String templateId) throws Exception {
        ProcessTemplates processTemplates = this.getById(templateId);
        if (ObjectUtils.isEmpty(processTemplates)) {
            throw new Exception("模版不存在");
        }
        processTemplates.setLogo(processTemplates.getIcon());
        processTemplates.setFormId(processTemplates.getTemplateId());
        processTemplates.setFormName(processTemplates.getTemplateName());
        return processTemplates;
    }

    @Override
    public void jsonToBpmn(FlowEngineDTO flowEngineDTO) throws Exception {
        String processJson = flowEngineDTO.getProcess();
        ChildNode childNode = JSONObject.parseObject(processJson, new TypeReference<ChildNode>() {
        });
        String settingsJson = flowEngineDTO.getSettings();
        SettingsInfo settingsInfo = JSONObject.parseObject(settingsJson, new TypeReference<SettingsInfo>() {
        });
        String remark = flowEngineDTO.getRemark();
        String formItems = flowEngineDTO.getFormItems();
        String formName = flowEngineDTO.getFormName();
        String logo = flowEngineDTO.getLogo();
        Long groupId = Long.valueOf(flowEngineDTO.getGroupId());
        String templateId = idWorker.nextId() + "";
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();


        ProcessTemplates processTemplates = ProcessTemplates.builder().build();
        processTemplates.setTemplateId(templateId);
        processTemplates.setTemplateName(formName);
        processTemplates.setTableName(flowEngineDTO.getTableName());
        processTemplates.setGroupId(groupId);
        processTemplates.setFormItems(formItems);
        processTemplates.setProcess(processJson);
        processTemplates.setIcon(logo);
        processTemplates.setBackground(logo);
        processTemplates.setNotify(settingsInfo.getNotify().toJSONString());
        String adminInfo = JSONObject.toJSONString(settingsInfo.getAdmin());
        processTemplates.setSettings(settingsJson);
        processTemplates.setWhoCommit(adminInfo);
        processTemplates.setWhoEdit(adminInfo);
        processTemplates.setWhoExport(adminInfo);
        processTemplates.setRemark(flowEngineDTO.getRemark());
        processTemplates.setIsStop(false);

        processTemplates.setCreateTime(new Date());
        processTemplates.setLastUpdateTime(new Date());

        if (Objects.nonNull(currentUser)) {
            processTemplates.setCreateUser(currentUser.getUserId());
            processTemplates.setLastUpdateUser(currentUser.getUserId());
        }
        String processCode = dingDingUtil.saveProcess(formName,flowEngineDTO.getRemark(),null);
        processTemplates.setProcessCode(processCode);
        this.save(processTemplates);
        TemplateGroup templateGroup = new TemplateGroup();
        templateGroup.setTemplateId(processTemplates.getTemplateId());
        templateGroup.setGroupId(groupId);
        templateGroup.setSortNum(0);
        templateGroup.setCreateTime(new Date());
        templateGroup.setLastUpdateTime(new Date());
        if (Objects.nonNull(currentUser)) {
            templateGroup.setCreateUser(currentUser.getUserId());
            templateGroup.setLastUpdateUser(currentUser.getUserId());
        }
        templateGroupService.save(templateGroup);

        JSONObject jsonObject = new JSONObject();
        jsonObject.put("processJson", processJson);
        jsonObject.put("formJson", formItems);
        BpmnModel bpmnModel = assemBpmnModel(jsonObject, childNode, remark, formName, groupId,
                templateId);
        repositoryService.createDeployment()
                .addBpmnModel(formName + ".bpmn", bpmnModel)
                .name(formName)
                .category(groupId + "")
                .deploy();

    }

    @Override
    public void updateFormDetail(ProcessTemplates template) {
        SettingsInfo settingsInfo = JSONObject.parseObject(template.getSettings(), new TypeReference<SettingsInfo>() {
        });
        JwtUserInfo currentUser = JwtUserInfo.fromHeader();
        ProcessTemplates processTemplates = this.getById(template.getFormId());
        processTemplates.setTemplateName(template.getFormName());
        processTemplates.setGroupId(template.getGroupId());
        processTemplates.setFormItems(template.getFormItems());
        processTemplates.setSettings(template.getSettings());
        processTemplates.setProcess(template.getProcess());
        processTemplates.setIcon(template.getIcon());
        processTemplates.setBackground(template.getBackground());
        processTemplates.setNotify(settingsInfo.getNotify().toJSONString());
        String adminInfo = JSONObject.toJSONString(settingsInfo.getAdmin());
        processTemplates.setWhoCommit(adminInfo);

        processTemplates.setWhoEdit(adminInfo);
        processTemplates.setWhoExport(adminInfo);
        processTemplates.setRemark(template.getRemark());
        if (Objects.nonNull(currentUser)) {
            processTemplates.setLastUpdateUser(currentUser.getUserId());
        }
        String processCode = dingDingUtil.saveProcess(template.getFormName(),template.getRemark(),processTemplates.getProcessCode());
        processTemplates.setProcessCode(processCode);
        this.updateById(processTemplates);
        ChildNode childNode = JSONObject.parseObject(template.getProcess(), new TypeReference<ChildNode>() {
        });
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("processJson", template.getProcess());
        jsonObject.put("formJson", template.getFormItems());
        BpmnModel bpmnModel = assemBpmnModel(jsonObject, childNode, template.getRemark(),
                template.getFormName(), template.getGroupId(), template.getFormId());
        repositoryService.createDeployment()
                .addBpmnModel(template.getFormName() + ".bpmn", bpmnModel)
                .name(template.getFormName())
                .category(template.getGroupId() + "")
                .deploy();
    }

    @Override
    public List<TemplateVo> queryByTableName(String tableName) {
        return baseMapper.selectTemplateVoByTableName(tableName);
    }

    private BpmnModel assemBpmnModel(JSONObject jsonObject, ChildNode childNode, String remark,
                                     String formName, Long groupId, String templateId) {
        BpmnModel bpmnModel = new BpmnModel();
        List<SequenceFlow> sequenceFlows = Lists.newArrayList();
        Map<String, ChildNode> childNodeMap = new HashMap<>();
        bpmnModel.setTargetNamespace(groupId + "");
        ExtensionAttribute extensionAttribute = new ExtensionAttribute();
        extensionAttribute.setName("DingDing");
        extensionAttribute.setNamespace("http://flowable.org/bpmn");
        extensionAttribute.setValue(jsonObject.toJSONString());
        Process process = new Process();
        process.setId(WorkFlowConstants.PROCESS_PREFIX + templateId);
        process.setName(formName);
        process.setDocumentation(remark);
        process.addAttribute(extensionAttribute);
        bpmnModel.addProcess(process);

        StartEvent startEvent = BpmnModelUtils.createStartEvent();
        process.addFlowElement(startEvent);
        String lastNode = null;
        try {
            lastNode = BpmnModelUtils.create(startEvent.getId(), childNode, process, bpmnModel, sequenceFlows, childNodeMap);
        } catch (InvocationTargetException e) {
            e.printStackTrace();
            throw new FlowableException("操作失败");
        } catch (IllegalAccessException e) {
            e.printStackTrace();
            throw new FlowableException("操作失败");
        }
        EndEvent endEvent = BpmnModelUtils.createEndEvent();
        process.addFlowElement(endEvent);
        process.addFlowElement(BpmnModelUtils.connect(lastNode, endEvent.getId(), sequenceFlows, childNodeMap, process));
        List<FlowableListener> executionListeners = new ArrayList<>();
        FlowableListener flowableListener = new FlowableListener();
        flowableListener.setEvent(ExecutionListener.EVENTNAME_END);
        flowableListener.setImplementationType(ImplementationType.IMPLEMENTATION_TYPE_DELEGATEEXPRESSION);
        flowableListener.setImplementation("${processListener}");
        executionListeners.add(flowableListener);
        process.setExecutionListeners(executionListeners);
        new BpmnAutoLayout(bpmnModel).execute();
        return bpmnModel;
    }
}
